//---------------------------------------------------------------------
// Author: Alex K. Angelopoulos & Keith Hill
//
// Description: Start a new process.
//
// Creation Date: 2006-07-01
//---------------------------------------------------------------------
using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Management.Automation;

namespace Pscx.Commands
{
    [Cmdlet(VerbsLifecycle.Start, "Process", SupportsShouldProcess = true),
     Description("Starts a new process."),
     DetailedDescription("Starts a new process.  This cmdlet can be used with documents to invoke their default verb or the verb can be specified using the -Verb parameter.  You can also specify the -Credential parameter to run the process as a different user."),
     RelatedLinks("Get-Process"), RelatedLinks("Stop-Processs")]
    public class StartProcessCommand : PSCmdlet
    {
        private PSCredential _credentials;
        private bool _createNoWindow;
        private bool _useShellExecute = true;
        private bool _loadUserProfile;
        private bool _boost;
        private string _fileName;
        private string _workingDir;
        private string _arguments = String.Empty;
        private string _verb = String.Empty;
        private int _waitForExitTimeout = -1;
        private ProcessWindowStyle _windowStyle = ProcessWindowStyle.Normal;
        private ProcessPriorityClass _priority = ProcessPriorityClass.Normal;

        #region Parameter declarations
        
        [Alias("FileName", "PSPath"),
         Parameter(Position = 0,
                   HelpMessage = "Name of application if it is in the path, otherwise path to application or document or protocol string e.g. http://www.codeplex.com/PowerShellCX."),
         AcceptsWildcards(false)]
        public string Path
        {
            get { return _fileName; }
            set { _fileName = value; }
        } 

        [Parameter(Position = 1,
                   HelpMessage = "Optional string of arguments supplied to the process."),
         AcceptsWildcards(false)]
        public string Arguments
        {
            get { return _arguments; }
            set { _arguments = value; }
        }

        [Parameter(HelpMessage = "Invoke an item specific verb."),
         AcceptsWildcards(false)]
        public string Verb
        {
            get { return _verb; }
            set { _verb = value; }
        }

        [Parameter(HelpMessage = "The initial directory for the process to be started."),
         AcceptsWildcards(false)]
        public string WorkingDirectory
        {
            get { return _workingDir; }
            set { _workingDir = value; }
        } 

        [Credential,
         ValidateNotNull,
         Parameter(HelpMessage = "Specifies the credentials under which to run the process that is started.")]
        public PSCredential Credential
        {
            get { return _credentials; }
            set { _credentials = value; }
        }

        [Parameter(HelpMessage = "Do not use the operating system shell when starting the process.")]
        public SwitchParameter NoShellExecute
        {
            get { return (SwitchParameter)!_useShellExecute; }
            set { _useShellExecute = !(bool)value; }
        }

        [Parameter(HelpMessage = "Do not create a window for this process, even if it normally uses one.")]
        public SwitchParameter NoWindow
        {
            get { return (SwitchParameter)_createNoWindow; }
            set { _createNoWindow = (bool)value; }
        }

        [Parameter(HelpMessage = "Style of display for the application window if one is shown."),
         DefaultValue("Normal")]
        public ProcessWindowStyle WindowStyle
        {
            get { return _windowStyle; }
            set { _windowStyle = value; }
        }

        [Parameter(HelpMessage = "Loads the user's profile from the HKEY_USERS registry key. Loading the " +
                                 "profile can be time-consuming, so it is best to use this value only if " +
                                 "you must access the information in the HKEY_CURRENT_USER registry key.")]
        public SwitchParameter LoadUserProfile
        {
            get { return (SwitchParameter)_loadUserProfile; }
            set { _loadUserProfile = (bool)value; }
        }

        [ValidateRange(0, int.MaxValue),
         Parameter(HelpMessage = "Wait until the process terminates or the time in seconds expires before " +
                                 "returning.  Use 0 seconds for infinite timeout."),
         DefaultValue("N/A")]
        public int WaitForExitTimeout
        {
            get { return _waitForExitTimeout; }
            set { _waitForExitTimeout = value; }
        }

        [Alias("PriorityBoostEnabled"),
         Parameter(HelpMessage = "Gets or sets a value indicating whether the associated process _priority " +
                                 "should temporarily be boosted when the main window has the focus.")]
        public bool Boost
        {
            get { return _boost; }
            set { _boost = value; }
        } 

        [Alias("PriorityClass"),
         Parameter(HelpMessage = "Indicates the priority that the system associates with a process. This value, " +
                                 "together with the priority value of each thread of the process, determines each thread's base priority level."),
         DefaultValue("Normal")]
        public ProcessPriorityClass Priority
        {
            get { return _priority; }
            set { _priority = value; }
        }

        #endregion Parameters

        protected override void BeginProcessing()
        {
            if (WildcardPattern.ContainsWildcardCharacters(_fileName))
            {
                ArgumentException ex = new ArgumentException("Illegal characters in path");
                ThrowTerminatingError(new ErrorRecord(ex, "IllegalCharsInPath", ErrorCategory.InvalidArgument, _fileName));
            }
            else
            {
                try
                {
                    _fileName = this.GetUnresolvedProviderPathFromPSPath(_fileName);
                }
                catch (Exception ex)
                {
                    WriteDebug("Could not resolve Path parameter: " + ex.Message);
                }
            }

            if (_credentials != null)
            {
                // If using credentials, UseShellExecute must be set to false
                _useShellExecute = false;
            }

            if (_workingDir == null)
            {
                _workingDir = this.SessionState.Path.CurrentFileSystemLocation.Path;
            }
        }

        protected override void ProcessRecord()
        {
            WriteDebug("Entering Start-Process.ProcessRecord()");

            ProcessStartInfo startInfo = new ProcessStartInfo(_fileName, _arguments);
            startInfo.Verb = _verb;
            startInfo.CreateNoWindow = _createNoWindow;
            startInfo.LoadUserProfile = _loadUserProfile;
            startInfo.UseShellExecute = _useShellExecute;
            startInfo.WindowStyle = _windowStyle;
            startInfo.WorkingDirectory = _workingDir;
            if (_credentials != null)
            {
                startInfo.Domain = _credentials.GetNetworkCredential().Domain;
                startInfo.UserName = _credentials.GetNetworkCredential().UserName;
                startInfo.Password = _credentials.Password;
            }

            if (ShouldProcess(String.Format("Path:'{0}', Args:'{1}', WorkingDir:'{2}'", _fileName, _arguments, _workingDir)))
            {
                try
                {
                    Process process = new Process();
                    process.StartInfo = startInfo;
                    StartProcess(process);
                }
                catch (Exception ex)
                {
                    ThrowTerminatingError(new ErrorRecord(ex, "FailedToStartProcess", ErrorCategory.NotSpecified, startInfo));
                }
            }

            WriteDebug("Leaving Start-Process.ProcessRecord()");
        }

        private void StartProcess(Process process)
        {
            if (process.Start())
            {
                if (process.PriorityClass != _priority)
                { 
                    process.PriorityClass = _priority; 
                }

                process.PriorityBoostEnabled = _boost;

                if (_waitForExitTimeout == 0)
                {
                    // Infinite wait
                    process.WaitForExit();
                }
                else if (_waitForExitTimeout > 0)
                {
                    process.WaitForExit(_waitForExitTimeout * 1000);
                }

                WriteObject(process);
            }
        }
    }
}
